// components/draw-board/draw-board.js
import Point from './Point';
import Line from './Line';
import Operate from './Operate';
import { TYPE } from "../../constant/constant";
import { addOperate, revokeOperate, resetOperate, updateImgId } from '../../api/api'

const app = getApp();
const dpr = app.globalData.dpr;
let Canvas;
let ctx;

Component({
  /**
   * 组件的属性列表
   */
  properties: {
    drawBoardId: {
      type: String,
      value: ''
    },
    type: {
      type: String,
      value: TYPE.DRAW
    },
    color: {
      type: String,
      value: 'rgb(0, 0, 0)'
    },
    size: {
      type: Number,
      value: 1
    },
    bgColor: {
      type: String,
      value: '#ffffff'
    },
    operates: {
      type: Array,
      value: []
    },
    version: {
      type: Number,
      value: 0
    },
  },

  /**
   * 组件的初始数据
   */
  data: {
    curLine: null,
    imgPath: null,
    showImg: false,
    curVersion: 0,
    curOperatesIdxs: [],
    operateQueue: [], // 操作队列
  },

  lifetimes: {
    attached() {
    },
    ready() {
      this.createSelectorQuery()
        .select('.board-canvas')
        .fields({
          node: true,
          size: true,
        })
        .exec((res) => {
          Canvas = res[0].node;
          ctx = Canvas.getContext('2d');
          // 解决自定义宽高比例不正确问题
          Canvas.width = res[0].width * dpr
          Canvas.height = res[0].height * dpr
          ctx.scale(dpr, dpr);
          this.init();
          this.data.curVersion = this.data.version;
        })
    }
  },
  observers: {
    'operates, version': function (operates, version) {
      if (this.data.curVersion < version) {
        // 在 operates 被修改时, 触发 drawAll 事件
        if (ctx) {
          this.drawAll();
          this.data.curVersion = version;
        }
      }
    },
    operateQueue: async function (operateQueue) {
      if (operateQueue.length && operateQueue[0].status === 0) {
        // 往数据库新增操作
        this.data.curVersion++;
        this.data.operateQueue[0].status = 1
        let { index } = await addOperate(this.data.drawBoardId, operateQueue[0].operate)
        // 更新为准确的 index 值
        this.data.curOperatesIdxs[this.data.curOperatesIdxs.indexOf(operateQueue[0].index)] = index;
        operateQueue.shift();
        this.setData({
          operateQueue,
        })
      }
    }
  },
  /**
   * 组件的方法列表
   */
  methods: {
    init() {
      ctx.lineCap = 'round'; // 让线条圆润
      // 设置背景颜色
      ctx.fillStyle = this.data.bgColor;
      ctx.fillRect(0, 0, Canvas.width, Canvas.height);
      ctx.fill();
    },
    onStart(e) {
      this.data.curLine = new Line(this.data.color, this.data.size);
      const { x, y } = e.touches[0];
      let point = new Point(x, y);
      this.data.curLine.addPoint(point);
    },
    onMove(e) {
      const { x, y } = e.touches[0];
      let point = new Point(x, y);
      let prePoint = this.data.curLine.getLastPoint();
      this.draw(prePoint, point, this.data.color, this.data.size);
      this.data.curLine.addPoint(point);
    },
    async onEnd(e) {
      let operate = new Operate(this.data.type, this.data.curLine)
      operate = this.serialize(operate);
      this.data.operates.push(operate);
      this.data.curOperatesIdxs.push(this.data.operates.length - 1);

      let operateQueue = this.data.operateQueue;
      operateQueue.push({
        index: this.data.operates.length - 1,
        status: 0, // 0 未上传服务器 1 正在上传服务器
        operate,
      })
      this.setData({
        operateQueue
      })
    },
    /**
     * 画线
     * @param {*} beginPoint 开始点
     * @param {*} endPoint 结束点
     * @param {*} color 颜色
     */
    draw(beginPoint, endPoint, color, size) {
      ctx.strokeStyle = color;
      ctx.lineWidth = size;
      ctx.beginPath();
      ctx.moveTo(beginPoint.x, beginPoint.y);
      ctx.lineTo(endPoint.x, endPoint.y);
      ctx.stroke();
      ctx.closePath();
    },
    // 重置画板
    async reset() {
      this.data.operates = [];
      this.init();

      // 往数据库重置操作
      this.data.curVersion++;
      await resetOperate(this.data.drawBoardId)
    },
    // 撤回上一步操作
    async revoke() {
      if (!this.data.curOperatesIdxs.length) return;
      let lastIndex = this.data.curOperatesIdxs.pop();

      // 如果现在任务队列里有任务还未执行, 直接将那个任务 pop 出
      let operateQueueLength = this.data.operateQueue.length;
      if (operateQueueLength && this.data.operateQueue[operateQueueLength - 1].status === 0) {
        let operateQueue = this.data.operateQueue;
        operateQueue.pop();
        this.setData({
          operateQueue
        })
        this.data.operates.pop();
        this.drawAll();
        return;
      }

      let lastOperate = this.deserialize(this.data.operates[lastIndex]);
      lastOperate.isDelete = true;
      this.data.operates[lastIndex] = this.serialize(lastOperate);
      this.drawAll();

      // 往数据库撤销上一步操作
      this.data.curVersion++;
      await revokeOperate(this.data.drawBoardId, lastIndex)
    },
    // 从头开始绘制一遍
    drawAll: function () {
      this.init();
      const operates = this.data.operates;
      for (let i = 0; i < operates.length; i++) {
        let operate = typeof operates[i] === "string" ? this.deserialize(operates[i]) : operates[i];
        let { isDelete, line } = operate;
        if (isDelete) continue;
        const { points } = line;
        for (let j = 0; j < points.length - 1; j++) {
          let beginPoint = points[j];
          let endPoint = points[j + 1];
          this.draw(beginPoint, endPoint, line.color, line.size);
        }
      }
    },
    // 隐藏 canvas, 展示 img
    async hideCanvas() {
      try {
        const res = await this.canvasToImg();
        this.setData({
          imgPath: res.tempFilePath,
          showImg: true,
        })
      } catch (err) {
        console.error(err);
      }
    },
    // 展示 canvas
    showCanvas() {
      this.setData({
        showImg: false
      })
    },
    /**
     * canvas 保存为图片
     * @param {boolean} isThumbnail 是否是缩略图, 是的话按固定大小 300*240
     */
    canvasToImg(isThumbnail = false) {
      // 目标图片的压缩比
      let radio = isThumbnail ? 0.4 : 1;
      return new Promise((resolve, reject) => {
        wx.canvasToTempFilePath({     //将canvas生成图片
          canvas: Canvas,
          width: Canvas.width,
          height: Canvas.height,
          destWidth: Canvas.width * radio,
          destHeight: Canvas.height * radio,
          success: function (res) {
            resolve(res)
          },
          fail: function (err) {
            reject(err)
          }
        }, this)
      })
    },
    // 保存图片到本地
    async saveImg() {
      try {
        const res = await this.canvasToImg();
        wx.saveImageToPhotosAlbum({
          filePath: res.tempFilePath,
          success: (res) => {
            wx.showToast({
              title: '保存图片成功',
              icon: 'success',
              duration: 2000
            })
          },
          fail: (err) => {
            wx.showToast({
              title: '保存图片失败',
              icon: 'error',
              duration: 2000
            })
          }
        })
      } catch (err) {
        wx.showToast({
          title: '保存图片失败',
          icon: 'error',
          duration: 2000
        })
      }
    },
    // 上传图片到云存储
    async updateImg() {
      return new Promise(async (resolve, reject) => {
        try {
          const res = await this.canvasToImg(true);
          const that = this;
          wx.cloud.uploadFile({
            cloudPath: `${this.data.drawBoardId}-${this.data.curVersion}.png`,
            filePath: res.tempFilePath,
            success: async res => {
              // 将 fileId 存储到画板数据中
              await updateImgId(that.data.drawBoardId, res.fileID)
              resolve();
            },
            fail: err => {
              reject(err)
            }
          })
        } catch (err) {
          reject(err)
        }
      })
    },
    getCurVersion() {
      return this.data.curVersion;
    },
    // operates 序列化
    serialize(operate) {
      return JSON.stringify(operate);
    },
    // operates 反序列化
    deserialize(operate) {
      return JSON.parse(operate);
    }
  }
})
